#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/semaphore.h>
#include <linux/kallsyms.h>
#include <linux/crypto.h>
#include <asm/pgtable_types.h>
#include <asm/cacheflush.h>


unsigned long * sys_call_table;

struct crypto_comp * comp;

#define __NR_lzoopt 171  /*origin sys_call:setdomainname*/

int can_write;
char compr_name[] = "lzo";

asmlinkage long (* origin_syscall)(unsigned long arg1, unsigned long arg2);
static void set_page_rw(unsigned long  _addr)
{   
    unsigned int level;
    pte_t *pte = lookup_address(_addr,&level);

    if (pte->pte & ~_PAGE_RW) 
        pte->pte |= _PAGE_RW;
}

static void set_page_ro(unsigned long _addr)
{   
    unsigned int level;
    pte_t *pte = lookup_address(_addr,&level);
    pte->pte &= ~_PAGE_RW;
}

static int isWritable(unsigned long _addr)
{
    unsigned int level;
    pte_t *pte = lookup_address(_addr,&level);
    if (pte->pte & _PAGE_RW){
        return 1;
    }else
        return 0;
}

static int compr_init(const char *algname)
{
    if(!crypto_has_comp(algname,0,0)){
        printk("there is not %s compress alg.\n",algname);
        return -1;
    }
    comp = crypto_alloc_comp(algname,0,0);
    if (IS_ERR(comp)){
        printk("canot initialize compressor %s, error %ld",
                algname, PTR_ERR(comp));
        return PTR_ERR(comp);
    }
    return 0;
}

static void compr_exit(void)
{
    crypto_free_comp(comp);
    return;
}

asmlinkage long sys_lzoopt(int opt_num,const char *src,
                        unsigned int slen,
                        char *dst,unsigned int *dlen)
{
    /*
     *opt_num: 0  ----> compress
     *         1  ----> decompress
     */
    int ret;
    if (opt_num == 0){
        ret = crypto_comp_compress(comp,src,slen,dst,dlen);
        if(ret != 0){
            printk("compress ret:%d there is something wrong\n",ret);
        }
        printk("compress ret:%d\n",ret);
        return ret;
    }else if(opt_num == 1){
        ret = crypto_comp_decompress(comp,src,slen,dst,dlen);
        if(ret != 0 ){
            printk("decompress ret:%d there is something wrong\n",ret);
        }
        printk("decompress ret:%d\n",ret);
        return ret;
    }else{
        printk("un supported opt number\n");
        return -1;
    }
    //printk("received argu:%d\n",opt_num);
    //return opt_num +1;
}




static int __init lkp_init(void)
{
    char *module_name = "sys_call_table";
    sys_call_table = (unsigned long *)kallsyms_lookup_name(module_name);
    can_write = isWritable((unsigned long)sys_call_table);
    printk("sys_call_table addr:0x%2.2lx  %s\n",(unsigned long)sys_call_table,can_write == 1 ? "Writable":"Unwritable");
    if(sys_call_table != 0){
        origin_syscall = (long (*)(unsigned long,unsigned long))sys_call_table[__NR_lzoopt];
        printk("modified address:0x%2.2lx\n",(unsigned long)(sys_call_table + __NR_lzoopt));
        set_page_rw((unsigned long)sys_call_table);
        can_write = isWritable((unsigned long)sys_call_table);
        printk("sys_call_table addr:0x%2.2lx  %s\n",(unsigned long)sys_call_table,can_write == 1 ? "Writable":"Unwritable");  
        
        sys_call_table[__NR_lzoopt] =(unsigned long)sys_lzoopt;
        
        set_page_ro((unsigned long)sys_call_table);
        can_write = isWritable((unsigned long)sys_call_table);
        printk("sys_call_table addr:0x%2.2lx  %s\n",(unsigned long)sys_call_table,can_write == 1 ? "Writable":"Unwritable");  
        if(compr_init(compr_name) != 0){
            printk("error in compr init\n");
            return -1;
        }else{
            printk("init compressor OK\n");
        }
    }else{
        printk("get sys_call_table error\n");
        return -1;
    }
    printk("(1) Hello World ! from the kernel space\n");
    return 0;
}

static void __exit lkp_cleanup(void)
{
    if(sys_call_table != 0){
        set_page_rw((unsigned long)sys_call_table);
        can_write = isWritable((unsigned long)sys_call_table);
        printk("sys_call_table addr:0x%2.2lx  %s\n",(unsigned long)sys_call_table,can_write == 1 ? "Writable":"Unwritable");
        
        sys_call_table[__NR_lzoopt] = (unsigned long)origin_syscall;
        
        set_page_ro((unsigned long)sys_call_table);
        can_write = isWritable((unsigned long)sys_call_table);
        printk("sys_call_table addr:0x%2.2lx  %s\n",(unsigned long)sys_call_table,can_write == 1 ? "Writable":"Unwritable");
     
    }
    compr_exit();
    printk("(1)Goodbye,world ! Leaving kernel space \n");

}

module_init(lkp_init);

module_exit(lkp_cleanup);
MODULE_LICENSE("GPL");
